package 读写锁;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 为什么需要读写锁   因为在有些业务场景中    会进行多读取 少写的情况 这个时候用普通的 lock和synchornzed 就不行的 因为他们只能保证同一时刻只有一个线程运行程序。
 * 也就是互斥锁
 * 共享锁
 * 所以用 读写锁  会让多个人共享读取锁   一个人只能进行写入   这样就能大大提高效率。
 *
 *
 *
 *          运用案例主要用于缓存
 */
public class ReaderWriterLockTest {

//    卖票业务   有的人查看是否有票    有的人去进行购买票
    //  读操作 - 读操作 共享
    // 读操作 - 写操作 互斥   同一时间只能有一个
    // 写操作 - 写操作  互斥  同一时间只能有一个


    public static void main(String[] args) {
        ticket ticket = new ticket();


        // 20个线程进行写操作
        for (int i = 1; i <= 1000; i++) {
            new Thread(() -> {
                ticket.buyTicket();
            },i+"").start();
        }

        // 20个线程进行读操作
        for (int i = 1; i <= 1000; i++) {
            new Thread(() -> {
                ticket.showTicket();
            },i+"").start();
        }





    }



   static class ticket{

        private volatile int ticketNum = 100;

        private ReentrantReadWriteLock lock= new ReentrantReadWriteLock();
//        读锁 也叫共享锁 同一时间能多个线程同时拥有锁
        private ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
//        写锁 也叫互斥锁 同一时间只能允许一个线程拥有锁
        private ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
        public void buyTicket() {
            //卖票
            writeLock.lock();
            //执行业务
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+"买到了票：剩余："+--ticketNum);
            //取消写锁
            writeLock.unlock();
        }

        public void showTicket(){
//           上读锁   只能有读需求的才能一起进行读取    todo 而写需求需要等待所以读完成后才能进行获取锁  或者相反情况
//           todo 锁降级
            readLock.lock();
            System.out.println(Thread.currentThread().getName()+"准备查看票！");
            System.out.println(Thread.currentThread().getName()+"查看了票  剩余："+ticketNum);
            readLock.unlock();
        }
    }
}
